#include <cstdio>
#include <ctime>
#include <string>
#include <cstdlib>
#include <memory>
#include <functional>

#ifdef DEBUG
    #include <iostream>
#endif

extern "C"{
    #include <unistd.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netinet/tcp.h>
    #include <netinet/ip.h>
    #include <net/ethernet.h>
    #include <pcap.h>
}

#include "ch_task.hpp"
#include "thread_pool.hpp"



#define MAX_CAP_BYTE 1500

static unsigned int ch_count = 0;

// pcap_loop() 的回调函数，包分析与处理函数
void capture(u_char *user, const struct pcap_pkthdr *packet_header, const u_char *packet_content);
// 数据包格式化输出函数
void print_packet(int packet_id, const struct pcap_pkthdr *packet_header, const u_char *packet_content);

int main(int argc, char **argv){
    pcap_t *handle;                             // 会话句柄
    char *device;                               // 设备

    char errbuf[PCAP_ERRBUF_SIZE] = {0};        // 存储错误信息，其中 PCAP_ERRBUF_SIZE 为宏定义的错误缓冲区大小
    struct bpf_program filter;                  // 已经编译好的过滤器

    in_addr ip_addr;                        // 主机的IP地址
    in_addr ip_mask;                        // 所在网络掩码
    
    int res = 0;                                // 暂存各种函数的返回值，用来判断成功与否

    // 给执行此程序的普通用户拥有操作网卡捕捉数据的权限
    uid_t uid = getuid();
    // 执行 setuid(0) 成功的前提：以管理员身份编译；权限设为 chmod u+s main
    setuid(0);
    
    // tcp[20] = 22 and tcp[25] = 1 and tcp[2:2] = 443
    // 过滤规则 // 
    #ifdef DEBUG
        const char * dev_to_open = "ens33";
        char filter_rules[] = "tcp[20] = 22 and tcp[25] = 1 and tcp[2:2] = 443 and host 192.168.47.1";
    #else
        const char * dev_to_open = argv[1];
        char filter_rules[] = "tcp[20] = 22 and tcp[25] = 1 and tcp[2:2] = 443";
    #endif

    // 查找网络设备，得到可用的网络设备名指针,ip_addr是这个设备IP的网络地址部份，ip_mask是这个设备IP的网络掩码
    res = pcap_lookupnet(dev_to_open, &(ip_addr.s_addr), &(ip_mask.s_addr), errbuf);
    if(res == -1){
        fprintf(stderr, "Couldn't find default device%s: %s\n", dev_to_open, errbuf);
        exit(1);
    }else{
        fprintf(stdout, "Susscessful lookup: \t\t%s\n", dev_to_open);
    }

    handle = pcap_open_live(dev_to_open, MAX_CAP_BYTE, 0, 0, errbuf);
    if(handle == NULL){
        fprintf(stderr, "Couldn't open device %s: %s\n", dev_to_open, errbuf);
        exit(1);
    }else{
        fprintf(stdout, "Susscessful open: \t\t%s\n", dev_to_open);
    }

    if (pcap_datalink(handle) != DLT_EN10MB) {
		fprintf(stderr, "Device %s doesn't provide Ethernet headers - not supported\n", dev_to_open);
		exit(1);
	}

    char p_ip[INET_ADDRSTRLEN];
    const char * p1 = inet_ntop(AF_INET, &(ip_addr.s_addr), p_ip, sizeof(p_ip));
    fprintf(stdout, "Internet IP ADDR of %s: \t%s\n", dev_to_open, p1);

    const char * p2 = inet_ntop(AF_INET, &(ip_mask.s_addr), p_ip, sizeof(p_ip));
    fprintf(stdout, "IP MASK of %s: \t\t%s\n", dev_to_open, p2);

    // 将用户制定的过滤策略编译到过滤程序中
    if (pcap_compile(handle, &filter, filter_rules, 0, ip_addr.s_addr) == -1) {
        fprintf(stderr, "Error calling pcap_compile:%s\n", filter_rules);
        exit(1);
    }

    // 设置过滤器
    if (pcap_setfilter(handle, &filter) == -1) {
        fprintf(stderr, "Error setting filter\n");
        exit(1);
    }

    // 执行数据包任务的执行器
    thread_pool executor;
    // 把执行完的任务的结果写入一个队列中
    thread_safe_queue<ch_info> queue;
    // capture 函数的参数，这三个对象都放在栈中
    ch_task_cap_para<ch_info> para(&executor, &queue);

    puts("Capturing...\n\n");

    // 捕包 -1 代表无限循环
    pcap_loop(handle, -1, capture, (u_char *)&para);
    // 关闭网络设备，释放资源
    pcap_close(handle);
    return 0;
}

void capture(u_char *user, const struct pcap_pkthdr *packet_header, const u_char *packet_content) {
    struct ch_pkt_header * ch;
    char * ja3;
    uint32_t ja3_len = 0;
    // 获得加密套件的个数
    uint32_t cipher_size;
    // 填充ja3的加密套件部分
    int add_flag;
    // 扩展字段总长度
    uint32_t exts_len;
    uint32_t exts_read;

    #ifdef DEBUG
        std::cout << ch_count << std::endl;
        // printf("%d\n", ch_count);
    #endif
    
    // 以太网头14字节，ip头20字节，tcp头20字节,一共54字节,小于54字节的数据包不管
    if(packet_header->caplen < 54){
        fprintf(stdout, "%s\n", "Bad Packet!");
        return;
    }

    // 报文实际的长度和抓取的报文长度不同，数据包错误
    if(packet_header->caplen != packet_header->len){
        fprintf(stdout, "%s\n", "packet_header->caplen != packet_header->len");
        return;
    }
    
    std::shared_ptr<ch_pkt> p = std::make_shared<ch_pkt>((const char *)packet_content, packet_header);

    auto fun_task = std::bind(run_ch_task, p, ((ch_task_cap_para<ch_info> *)user)->queue);

    // 把这个数据包处理任务放入线程池的任务队列中
    ((ch_task_cap_para<ch_info> *)user)->executor->submit(fun_task);

    // fun_task();

    // 收到的有效的client hello的数量
    ++ch_count;

    #ifdef DEBUG
        fflush(stdout);
    #endif
}
